package hooks

import (
	"context"
	"fmt"
	"os"
	"strings"

	log "atomgit.com/atomgit_dev/alog/alogcore"
	"atomgit.com/atomgit_dev/git-hooks/pkg/config"
	"atomgit.com/atomgit_dev/git-hooks/pkg/internal/agitext"
	"atomgit.com/atomgit_dev/git-hooks/pkg/stream"
	"atomgit.com/atomgit_dev/git-hooks/pkg/streamio"
	pb "atomgit.com/atomgit_dev/satellite-proto/go"
)

// StartPostReceive calls satellite.PostReceiveHook to execute post-receive hook.
func StartPostReceive(ctx context.Context) (int, error) {
	params, err := agitext.GetSpaceXParamsFromEnv()
	if err != nil {
		return 1, fmt.Errorf("fail to get spacex params from env: %w", err)
	}
	if params.Enabled && !params.MainHooker {
		// skip post-receive hook if spacex is enabled but not the main hooker
		log.Info(ctx, "skip post-receive hook intentionally")
		return 0, nil
	}

	repository, err := agitext.GetPbRepositoryFromEnv()
	if err != nil {
		return 1, fmt.Errorf("fail to extract pb.Repository from env: %w", err)
	}
	keyID := os.Getenv("GL_ID")
	gitPushOptions := getPushOptionsFromEnv()
	log.Info(ctx, "begin to execute post-receive hook",
		log.NewStringField("pushopts", strings.Join(gitPushOptions, ",")),
		log.NewStringField("user", keyID),
		log.NewInt64Field("RepositoryID", repository.GlRepositoryId),
	)

	conn, err := getSatelliteConn()
	if err != nil {
		return 1, fmt.Errorf("fail to create grpc client for post-receive: %w", err)
	}
	defer conn.Close()

	ctx, cancel := context.WithTimeout(ctx, config.Cfg.PostReceiveTimeout)
	defer cancel()
	postReceiveHookStream, err := pb.NewHookServiceClient(conn).PostReceiveHook(ctx)
	if err != nil {
		return 1, fmt.Errorf("fail to get post-receive hook stream: %w", err)
	}

	if err := postReceiveHookStream.Send(&pb.PostReceiveHookRequest{
		Repository:     repository,
		KeyId:          keyID,
		GitPushOptions: gitPushOptions,
	}); err != nil {
		return 1, fmt.Errorf("fail to send post-receive hook request: %w", err)
	}

	f := sendFunc(streamio.NewWriter(func(p []byte) error {
		return postReceiveHookStream.Send(&pb.PostReceiveHookRequest{Stdin: p})
	}), postReceiveHookStream, os.Stdin)

	returnCode, err := stream.Handler(func() (stream.StdoutStderrResponse, error) {
		return postReceiveHookStream.Recv()
	}, f, os.Stdout, os.Stderr)
	if err != nil {
		return 1, fmt.Errorf("error receiving data for post-receive hook: %v", err)
	}

	return int(returnCode), nil
}
